let activeEffect
let effectStack = []
/**
 * 关于依赖
 * 从数据的角度,依赖是指依赖该属性的[副作用函数]
 */
function cleanup (effectFn) {
  for (let i = 0; i < effectFn.deps.length; i++) {
    effectFn.deps[i].delete(effectFn)
  }
  effectFn.deps.length = 0
}
function effect (fn, options = {}) {
  const effectFn = () => {
    console.log('effectFn executed')
    cleanup(effectFn)
    activeEffect = effectFn
    effectStack.push(effectFn)
    fn()
    effectStack.pop()
    activeEffect = effectStack[effectStack.length - 1]
  }
  effectFn.options = options
  effectFn.deps = []
  effectFn()
}

/**
 * 副作用函数中被读取的数据必须被代理
 */
//全局响应式存放的中心
let bucket = new WeakMap()
const TriggerType = {
  SET: 'SET',
  ADD: 'ADD',
  DELETE: 'DELETE'
}
function track (target, key) {
  if (!activeEffect) return target[key]
  let depsMap = bucket.get(target)
  if (!depsMap) {
    bucket.set(target, (depsMap = new Map()))
  }
  let deps = depsMap.get(key)
  if (!deps) {
    depsMap.set(key, (deps = new Set()))
  }
  deps.add(activeEffect)
  activeEffect.deps.push(deps)
}

function trigger (target, key, type) {
  let depsMap = bucket.get(target)
  if (!depsMap) {
    return
  }
  const effects = depsMap.get(key)
  let effectsToRun = new Set()
  effects &&
    effects.forEach(effectFn => {
      if (effectFn != activeEffect) {
        effectsToRun.add(effectFn)
      }
    })
  if (type == 'ADD' || type == 'DELETE') {
    const iterateEffects = depsMap.get(ITERATE_KEY)
    iterateEffects &&
      iterateEffects.forEach(effectFn => {
        if (effectFn != activeEffect) {
          effectsToRun.add(effectFn)
        }
      })
  }
  if (type == 'ADD' && Array.isArray(target)) {
    const lengthEffects = depsMap.get('length')
    lengthEffects &&
      lengthEffects.forEach(effectFn => {
        if (effectFn != activeEffect) {
          effectsToRun.add(effectFn)
        }
      })
  }
  if (Array.isArray(target) && key == 'length') {
    depsMap.forEach((effects, key) => {
      if (key >= newVal) {
        effects.forEach(effectFn => {
          if (effectFn != activeEffect) {
            effectsToRun.add(effectFn)
          }
        })
      }
    })
  }

  effectsToRun.forEach(effectFn => {
    if (effectFn.options.scheduler) {
      effectFn.options.scheduler(effectFn)
    } else {
      effectFn()
    }
  })
}
const ITERATE_KEY = Symbol()
const arrayInstrumentations = {}[
  ('includes', 'indexOf', 'lastIndexOf')
].forEach(method => {
  const originMethod = Array.prototype[method]
  arrayInstrumentations[method] = function (...args) {
    let res = originMethod.apply(this, args)
    if (res == false || res == -1) {
      res = originMethod.apply(this.raw, args)
    }

    return res
  }
})
let shouldTrack = true[('push', 'pop', 'shift', 'unshift', 'splice')].forEach(
  method => {
    const originMethod = Array.prototype[method]
    arrayInstrumentations[method] = function (...args) {
      //屏蔽对lenght读取操作的依赖收集
      shouldTrack = false
      let res = originMethod.apply(this, args)
      shouldTrack = true
      return res
    }
  }
)
/**
 *
 * length 首先作为一个普通的属性，在被读取时应收集其依赖【读取length的副作用函数】
 * 被修改时触发相应的依赖执行
 * 问题的关键在于，要明确那些操作会修改length
 *
 * 执行操作时，收集相关依赖
 * 合理的触发收集的依赖
 */
function createReactive (data) {
  return new Proxy(data, {
    get (target, key, receiver) {
      
      if (key == 'raw') {
        return target
      }
      if (Array.isArray(target) && arrayInstrumentations.hasOwnProperty(key)) {
        return Reflect.get(arrayInstrumentations, key, receiver)
      }
      if (typeof key !== Symbol) {
        track(target, key)
      }
      return Reflect.get(target, key, receiver)
    },
    set (target, key, newVal, receiver) {
      const oldVal = target[key]

      const type = Array.isArray(target)
        ? Number(key) < target.length
          ? 'SET'
          : 'ADD'
        : Object.prototype.hasOwnProperty.call(target, key)
        ? 'SET'
        : 'ADD'

      const res = Reflect.set(target, key, newVal, receiver)
      if (target == receiver.raw) {
        if (oldVal !== newVal) {
          trigger(target, key, type)
        }
      }
      return res
    },
    ownKeys (target) {
      track(target, Array.isArray(target) ? 'length' : ITERATE_KEY)
      return Reflect.ownKeys(target)
    },
    deleteProperty (target, key) {
      const hadKey = Object.hasOwnProperty.call(target, key)
      const res = Reflect.deleteProperty(target, key)
      if (res && hadKey) {
        trigger(target, key, 'DELETE')
      }
    }
  })
}

export { trigger, track, effect, createReactive }
